classdef EffectReverbWindow < handle
    %%EFFECTREVERBWINDOW Dooku Synthesizer Reverb Effect Window GUI class
    properties
        % Figure variables
        hfig;
        handles;
        
        % Setup variables
        Data;
        Fs;
        
        % Play variables
        player;
        playStatus;
    end
    
    properties (SetObservable)
        finish;
    end
    
    methods
        
        function obj = EffectReverbWindow
            % Figure Load
            obj.hfig = hgload('EffectReverbGUI.fig');
            obj.handles = guihandles(obj.hfig);
            
            % Callbacks Definitions
            set(obj.handles.reverbWindow, 'CloseRequestFcn', @(src, event) finishSelect(obj, src, event));
            set(obj.handles.standardRad, 'Callback', @(src, event) roomTypeSelect(obj, src, event));
            set(obj.handles.customRad, 'Callback', @(src, event) roomTypeSelect(obj, src, event));
            set(obj.handles.roomsMenu, 'Callback', @(src, event) roomSelect(obj, src, event));
            set(obj.handles.finishBut, 'Callback', @(src, event) finishSelect(obj, src, event));
            bg_image = imread('play.png');
            set(obj.handles.play, 'Callback', @(src, event) play(obj, src, event), 'CData', bg_image);
            bg_image = imread('stop.png');
            set(obj.handles.stop, 'Callback', @(src, event) stop(obj, src, event), 'CData', bg_image);
            set(obj.handles.rd_x, 'Callback', @(src, event) roomDimChange(obj, src, event));
            set(obj.handles.rd_y, 'Callback', @(src, event) roomDimChange(obj, src, event));
            set(obj.handles.rd_z, 'Callback', @(src, event) roomDimChange(obj, src, event));
            set(obj.handles.mc_x, 'Callback', @(src, event) micCoordChange(obj, src, event));
            set(obj.handles.mc_y, 'Callback', @(src, event) micCoordChange(obj, src, event));
            set(obj.handles.mc_z, 'Callback', @(src, event) micCoordChange(obj, src, event));
            set(obj.handles.sc_x, 'Callback', @(src, event) sourceCoordChange(obj, src, event));
            set(obj.handles.sc_y, 'Callback', @(src, event) sourceCoordChange(obj, src, event));
            set(obj.handles.sc_z, 'Callback', @(src, event) sourceCoordChange(obj, src, event));
            set(obj.handles.rf, 'Callback', @(src, event) reflectCoeffChange(obj, src, event));
            set(obj.handles.ns, 'Callback', @(src, event) virtualSourcesChange(obj, src, event));
            set(obj.handles.calculateBut, 'Callback', @(src, event) calculateSelect(obj, src, event));
            
            obj.defaultVariables();
        end
        
        function defaultVariables(obj)
            obj.Data.name = 'Reverb';
            obj.Data.standardCustom = 1;
            obj.Data.roomInd = 1;
            obj.Data.impResp = wavread('Arena.wav'); %#ok<DWVRD>
            obj.Fs = 44100;
            obj.Data.impRespX = linspace(0, length(obj.Data.impResp)/obj.Fs, length(obj.Data.impResp));
            obj.Data.roomDim = [20, 19, 21];
            obj.Data.micCoord = [19, 18, 1.6];
            obj.Data.sourceCoord = [5, 2, 1];
            obj.Data.reflectCoeff = 0.3;
            obj.Data.virtualSources = 12;
            obj.playStatus = 0;
        end
        
        
        function visible(obj)
            set(obj.hfig, 'Visible', 'on');
            set(obj.hfig, 'Windowstyle', 'modal');
            obj.roomTypeSelect(0);
        end
        
        function refreshWindow(obj, dataIn)
            obj.Data = dataIn;
            set(obj.handles.rd_x, 'String', num2str(obj.Data.roomDim(1)));
            set(obj.handles.rd_y, 'String', num2str(obj.Data.roomDim(2)));
            set(obj.handles.rd_z, 'String', num2str(obj.Data.roomDim(3)));
            set(obj.handles.mc_x, 'String', num2str(obj.Data.micCoord(1)));
            set(obj.handles.mc_y, 'String', num2str(obj.Data.micCoord(2)));
            set(obj.handles.mc_z, 'String', num2str(obj.Data.micCoord(3)));
            set(obj.handles.sc_x, 'String', num2str(obj.Data.sourceCoord(1)));
            set(obj.handles.sc_y, 'String', num2str(obj.Data.sourceCoord(2)));
            set(obj.handles.sc_z, 'String', num2str(obj.Data.sourceCoord(3)));
            set(obj.handles.rf, 'String', num2str(obj.Data.reflectCoeff));
            set(obj.handles.ns, 'String', num2str(obj.Data.virtualSources));
            if obj.Data.standardCustom
                set(obj.handles.standardRad, 'Value', 1);
                set(obj.handles.customRad, 'Value', 0);
            else
                set(obj.handles.standardRad, 'Value', 0);
                set(obj.handles.customRad, 'Value', 1);
                obj.calculateSelect();
            end
            set(obj.handles.roomsMenu, 'Value', obj.Data.roomInd);
        end
        
        function refreshReverb(obj)
            plot(obj.handles.impulseAxes, obj.Data.impRespX, obj.Data.impResp, 'Color', [1,0,0]);
            xlim(obj.handles.impulseAxes, [obj.Data.impRespX(1), obj.Data.impRespX(end)]);
            set(obj.handles.impulseAxes, 'Color', [0 0 0], 'XColor', [0.9 0.9 0.9], 'YColor', [0.9 0.9 0.9], 'YTick', []);
            obj.player = audioplayer(obj.Data.impResp, obj.Fs);
            set(obj.player, 'StopFcn', @(src, event) finishPlay(obj, src, event));
            set(obj.handles.effectiveTime, 'String', num2str(round(reverbDuration(obj.Data.impResp, obj.Fs)*1000)));
        end
        
        function obj = roomTypeSelect(obj, src, ~)
            if src
                set(src, 'Value', 1);
            end
            if get(obj.handles.standardRad, 'Value')
                set(obj.handles.roomsMenu, 'Enable', 'On');
                set(obj.handles.rd_x, 'Enable', 'Off');
                set(obj.handles.rd_y, 'Enable', 'Off');
                set(obj.handles.rd_z, 'Enable', 'Off');
                set(obj.handles.mc_x, 'Enable', 'Off');
                set(obj.handles.mc_y, 'Enable', 'Off');
                set(obj.handles.mc_z, 'Enable', 'Off');
                set(obj.handles.sc_x, 'Enable', 'Off');
                set(obj.handles.sc_y, 'Enable', 'Off');
                set(obj.handles.sc_z, 'Enable', 'Off');
                set(obj.handles.rf, 'Enable', 'Off');
                set(obj.handles.ns, 'Enable', 'Off');
                set(obj.handles.calculateBut, 'Enable', 'Off');
                obj.Data.standardCustom = 1;
                obj.roomSelect();
            else
                set(obj.handles.roomsMenu, 'Enable', 'Off');
                set(obj.handles.rd_x, 'Enable', 'On');
                set(obj.handles.rd_y, 'Enable', 'On');
                set(obj.handles.rd_z, 'Enable', 'On');
                set(obj.handles.mc_x, 'Enable', 'On');
                set(obj.handles.mc_y, 'Enable', 'On');
                set(obj.handles.mc_z, 'Enable', 'On');
                set(obj.handles.sc_x, 'Enable', 'On');
                set(obj.handles.sc_y, 'Enable', 'On');
                set(obj.handles.sc_z, 'Enable', 'On');
                set(obj.handles.rf, 'Enable', 'On');
                set(obj.handles.ns, 'Enable', 'On');
                set(obj.handles.calculateBut, 'Enable', 'On');
                obj.Data.standardCustom = 0;
                obj.calculateSelect();
            end
            obj.refreshReverb();
        end
        
        function obj = roomSelect(obj, ~, ~)
            obj.Data.roomInd = get(obj.handles.roomsMenu, 'Value');
            roomsStr = get(obj.handles.roomsMenu, 'String');
            obj.Data.impResp = wavread(char(strcat(roomsStr(obj.Data.roomInd), '.wav'))); %#ok<DWVRD>
            obj.Data.impRespX = linspace(0, length(obj.Data.impResp)/obj.Fs, length(obj.Data.impResp));
            obj.refreshReverb();
        end
        
        function obj = finishSelect(obj, ~, ~)
            obj.finish = 1;
            set(obj.hfig, 'Visible', 'off');
            set(obj.hfig, 'Windowstyle', 'normal');
        end
        
        function obj = play(obj, ~, ~)
            if obj.playStatus % Pause
                obj.playStatus = 0;
                bg_image = imread('play.png');
                set(obj.handles.play, 'CData', bg_image);
                pause(obj.player);
            else % Play/Resume
                obj.playStatus = 1;
                bg_image = imread('pause.png');
                set(obj.handles.play, 'CData', bg_image);
                if isempty(obj.player) || (obj.player.currentSample() == 1) % Play
                    play(obj.player);
                else % Resume
                    resume(obj.player);
                end
            end
        end
        
        function obj = finishPlay(obj, ~, ~)
            obj.playStatus = 0;
            bg_image = imread('play.png');
            set(obj.handles.play, 'CData', bg_image);
        end
        
        function obj = stop(obj, ~, ~)
            obj.playStatus = 0;
            bg_image = imread('play.png');
            set(obj.handles.play, 'CData', bg_image);
            stop(obj.player);
        end
        
        function obj = roomDimChange(obj, ~, ~)
            rdAux = [str2double(get(obj.handles.rd_x, 'String')), str2double(get(obj.handles.rd_y, 'String')), str2double(get(obj.handles.rd_z, 'String'))];
            if sum(isnan(rdAux)) || sum(rdAux <= 0)
                errordlg('Room Dimensions must have positive numeric values.', 'Inconsistent error');
                set(obj.handles.rd_x, 'String', num2str(obj.Data.roomDim(1)));
                set(obj.handles.rd_y, 'String', num2str(obj.Data.roomDim(2)));
                set(obj.handles.rd_z, 'String', num2str(obj.Data.roomDim(3)));
            else
                obj.Data.roomDim = rdAux;
            end
        end
        
        function obj = micCoordChange(obj, ~, ~)
            mcAux = [str2double(get(obj.handles.mc_x, 'String')), str2double(get(obj.handles.mc_y, 'String')), str2double(get(obj.handles.mc_z, 'String'))];
            if sum(isnan(mcAux)) || sum(mcAux <= 0)
                errordlg('Microphone Coordinates must have positive numeric values.', 'Inconsistent error');
                set(obj.handles.mc_x, 'String', num2str(obj.Data.micCoord(1)));
                set(obj.handles.mc_y, 'String', num2str(obj.Data.micCoord(2)));
                set(obj.handles.mc_z, 'String', num2str(obj.Data.micCoord(3)));
            else
                obj.Data.micCoord = mcAux;
            end
        end
        
        function obj = sourceCoordChange(obj, ~, ~)
            scAux = [str2double(get(obj.handles.sc_x, 'String')), str2double(get(obj.handles.sc_y, 'String')), str2double(get(obj.handles.sc_z, 'String'))];
            if sum(isnan(scAux)) || sum(scAux <= 0)
                errordlg('Source Coordinates must have positive numeric values.', 'Inconsistent error');
                set(obj.handles.sc_x, 'String', num2str(obj.Data.sourceCoord(1)));
                set(obj.handles.sc_y, 'String', num2str(obj.Data.sourceCoord(2)));
                set(obj.handles.sc_z, 'String', num2str(obj.Data.sourceCoord(3)));
            else
                obj.Data.sourceCoord = scAux;
            end
        end
        
        function obj = reflectCoeffChange(obj, ~, ~)
            rfAux = str2double(get(obj.handles.rf, 'String'));
            if (isnan(rfAux)) || (rfAux <= 0)
                errordlg('Reflection Coefficient must be a positive numeric value.', 'Inconsistent error');
                set(obj.handles.rf, 'String', num2str(obj.Data.reflectCoeff));
            else
                obj.Data.reflectCoeff = rfAux;
            end
        end
        
        function obj = virtualSourcesChange(obj, ~, ~)
            nsAux = str2double(get(obj.handles.ns, 'String'));
            if (isnan(nsAux)) || (nsAux <= 0)
                errordlg('Number of virtual sources must be a positive numeric value.', 'Inconsistent error');
                set(obj.handles.ns, 'String', num2str(obj.Data.virtualSources));
            else
                obj.Data.virtualSources = nsAux;
            end
        end

        function obj = calculateSelect(obj, ~, ~)
            obj.Data.impResp = rir(obj.Fs, obj.Data.micCoord, obj.Data.virtualSources, obj.Data.reflectCoeff, obj.Data.roomDim, obj.Data.sourceCoord);
            obj.Data.impRespX = linspace(0, length(obj.Data.impResp)/obj.Fs, length(obj.Data.impResp));
            obj.refreshReverb();
        end
        
    end
    
end